package com.handydata.antifraud.retry;

import java.util.function.Consumer;
import java.util.function.Supplier;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.RecoveryCallback;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.stereotype.Component;

import com.handydata.antifraud.channel.outbound.MyRetryTemplate;
import com.handydata.antifraud.common.exception.RetryException;
import com.handydata.antifraud.enums.Comment;
import com.handydata.antifraud.enums.ProcessStatusEnum;

@Component
public class RetryExecutor {

    private static final Logger logger = LoggerFactory.getLogger(RetryExecutor.class);

    @Autowired
    private MyRetryTemplate myRetryTemplate;

    /**
     * 执行重试操作
     * @param pusher 推送操作
     * @param recorder 推送操作记录
     * @param <S>
     */
    public <S> void doRetry(Supplier<Boolean> pusher, Consumer<Comment> recorder) {
        // 通过RetryCallback 重试回调实例包装正常逻辑逻辑，第一次执行和重试执行执行的都是这段逻辑
        final RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() {
            //RetryContext 重试操作上下文约定，统一spring-try包装
            public Object doWithRetry(RetryContext context) throws Exception {
                boolean result = pusher.get();
                if(!result){
                  //logger.info("正在重试推送数据给上行...");
                  throw new RuntimeException();//这个点特别注意，重试的根源通过Exception返回
                }
                recorder.accept(ProcessStatusEnum.RETURN_OK);
                return true;
            }
        };
        // 通过RecoveryCallback 重试流程达到重试上限后的退出恢复操作实例
        final RecoveryCallback<Object> recoveryCallback = new RecoveryCallback<Object>() {
            public Object recover(RetryContext context) throws Exception {
                recorder.accept(ProcessStatusEnum.RETURN_FAIL);
                return null;
            }
        };
        try {
            // 由retryTemplate 执行execute方法开始逻辑执行
            myRetryTemplate.execute(retryCallback, recoveryCallback);
        } catch (Exception e) {
            logger.error("重试操作执行错误");
            throw new RetryException("重试操作执行错误");
        }
    }

}
